#include <reg51.h>
#include <stdio.h>

#define FOSC 11059200L // 晶振频率
#define T1_PERIOD 500 // 定时器1中断周期，单位：微秒
#define T1_RELOAD (65536 - (FOSC / 1000000 * T1_PERIOD)) // 定时器1重载值

// 用于保存PWM信号的高电平时间和低电平时间
unsigned int high_time = 0;
unsigned int low_time = 0;
// 用于保存PWM信号的频率和占空比
unsigned int freq = 0;
unsigned int duty = 0;
int i = 0;

//--定义使用的IO--//
#define GPIO_DIG P0

sbit LSA = P2 ^ 2;
sbit LSB = P2 ^ 3;
sbit LSC = P2 ^ 4;
sbit PWM_PIN = P3 ^ 5; // 与PWM输出引脚一致
sbit LED = P3 ^ 4; // 用于调试

//--定义全局变量--//
unsigned char code DIG_CODE[17] = {
    0x3f, 0x06, 0x5b, 0x4f, 0x66, 0x6d, 0x7d, 0x07,
    0x7f, 0x6f, 0x77, 0x7c, 0x39, 0x5e, 0x79, 0x71
};
//0、1、2、3、4、5、6、7、8、9、A、b、C、d、E、F的显示码
unsigned char DisplayData[8] = {0};

// 函数声明
void Timer1_Init(void);
//void Timer1_Isr(void) interrupt 3;
void DigDisplay(void);

void main()
{
    Timer1_Init(); // 初始化定时器1

    while (1)
    {
        // 将频率和占空比分解为单个数字，存储在DisplayData中
        unsigned int temp_freq = freq;
        unsigned int temp_duty = duty;
        for (i = 3; i >= 0; i--)
        {
            DisplayData[i] = DIG_CODE[temp_freq % 10];
            temp_freq /= 10;
        }
        for (i = 7; i >= 4; i--)
        {
            DisplayData[i] = DIG_CODE[temp_duty % 10];
            temp_duty /= 10;
        }
        DigDisplay(); // 显示频率和占空比
    }
}

void Timer1_Init(void) // 定时器1初始化函数
{
    TMOD |= 0x10; // 设置定时器1为模式1
    TH1 = T1_RELOAD >> 8; // 计算定时器初值高8位
    TL1 = T1_RELOAD & 0xff; // 计算定时器初值低8位
    ET1 = 1; // 允许定时器1中断
    TR1 = 1; // 启动定时器1
    EA = 1; // 开启总中断
}

void Timer1_Isr(void) interrupt 3 // 定时器1中断处理函数
{
    static unsigned int total_time = 0; // 用于保存PWM信号的总时间
    static unsigned int high_time_acc = 0; // 累积高电平时间
    static unsigned int sample_count = 0; // 采样次数
    static bit status = 0; // 用于判断当前是高电平还是低电平

    TH1 = T1_RELOAD >> 8; // 重新加载定时器初值高8位
    TL1 = T1_RELOAD & 0xff; // 重新加载定时器初值低8位

    if (status == 0) // 当前为低电平
    {
        if (!PWM_PIN) // 检测到高电平
        {
            LED = 0; // 点亮LED表示检测到高电平
            high_time_acc += T1_PERIOD; // 累积高电平时间
            status = 1; // 切换到高电平状态
        }
    }
    else // 当前为高电平
    {
        if (PWM_PIN) // 检测到低电平
        {
            LED = 1; // 点亮LED表示检测到低电平
            total_time += high_time_acc; // 累积总时间
            high_time += high_time_acc; // 累积高电平时间
            high_time_acc = 0; // 重置高电平累积时间
            status = 0; // 切换到低电平状态
            sample_count++;
            if (sample_count >= 100) // 每100次采样计算一次频率和占空比
            {
                freq = 1000000 / (total_time / sample_count); // 计算频率，单位：Hz
                duty = (high_time / sample_count) * 100 / (total_time / sample_count); // 计算占空比
                high_time = 0; // 重置高电平时间
                total_time = 0; // 重置总时间
                sample_count = 0; // 重置采样次数
            }
        }
    }
}

void DigDisplay(void)
{
    unsigned char i;
    unsigned int j;
    for (i = 0; i < 8; i++)
    {
        switch (i) // 位选，选择点亮的数码管
        {
        case (0):
            LSA = 0;
            LSB = 0;
            LSC = 0;
            break; // 显示第0位
        case (1):
            LSA = 1;
            LSB = 0;
            LSC = 0;
            break; // 显示第1位
        case (2):
            LSA = 0;
            LSB = 1;
            LSC = 0;
            break; // 显示第2位
        case (3):
            LSA = 1;
            LSB = 1;
            LSC = 0;
            break; // 显示第3位
        case (4):
            LSA = 0;
            LSB = 0;
            LSC = 1;
            break; // 显示第4位
        case (5):
            LSA = 1;
            LSB = 0;
            LSC = 1;
            break; // 显示第5位
        case (6):
            LSA = 0;
            LSB = 1;
            LSC = 1;
            break; // 显示第6位
        case (7):
            LSA = 1;
            LSB = 1;
            LSC = 1;
            break; // 显示第7位
        }
        GPIO_DIG = DisplayData[i]; // 发送段码
        j = 10; // 扫描间隔时间设定
        while (j--);
        GPIO_DIG = 0x00; // 消隐
    }
}